<?php
namespace Swiftx\Component\Orm\Statement;
use PDO;
use PDOStatement;
use Swiftx\Component\Orm\Exceptions\Runtime;
use Swiftx\Component\Orm\Exceptions\Error;
use Swiftx\Interfaces\Orm\Pager as InterfacePager;
use Swiftx\Interfaces\Orm\Builder as InterfaceBuilder;
use Swiftx\Interfaces\Orm\Entity;
use Swiftx\Interfaces\Orm\Manager;

/**
 * 属性抽象
 * @package Swiftx\Component\Orm\Reflection
 */
class Builder implements InterfaceBuilder {

    /**
     * SQL语句
     * @var string
     */
    protected $sql;

    /**
     * 实体类名
     * @var string
     */
    protected $entityClass;

    /**
     * 管理对象
     * @var Manager
     */
    protected $manager;

    /**
     * 连接实例
     * @var PDO
     */
    protected $connect;

    /**
     * 分页设置
     * @var string
     */
    protected $limit;

    /**
     * 查询条件
     * @var array
     */
    protected $where = [];

    /**
     * 查询条件分组情况
     * @var int
     */
    protected $whereGroupCount = 0;

    /**
     * 排序条件
     * @var array
     */
    protected $order = [];

    /**
     * 分组条件
     * @var array
     */
    protected $group = [];

    /**
     * 构造函数
     * @param Manager $manager
     */
    public function setManager(Manager $manager){
        $this->manager = $manager;
    }

    /**
     * 设置连接实例
     * @param PDO $connect
     */
    public function setConnect(PDO $connect){
        $this->connect = $connect;
    }

    /**
     * 设置实体类名
     * @param string $className
     */
    public function setEntity(string $className){
        $this->entityClass = $className;
    }

    /**
     * 获取字段名称
     * @param string $field
     * @param bool $escape
     * @return string
     */
    protected function getFieldName(string $field, bool $escape=true):string {
        $field = $this->manager->translateTableField($this->entityClass, $field);
        return $escape?'`'.$field.'`':$field;
    }

    /**
     * 获取表名称
     * @param bool $escape
     * @return string
     */
    protected function getTableName(bool $escape=true):string {
        $table = $this->manager->translateTableName($this->entityClass);
        return $escape?'`'.$table.'`':$table;
    }

    /**
     * 设置查询条件
     * @param string $sql
     * @param bool $and
     * @return InterfaceBuilder
     */
    public function conditionSql(string $sql, bool $and=true):InterfaceBuilder{
        if(!empty($this->where))
            $this->where[] = $and?'AND':'OR';
        $this->where[] = $sql;
        return $this;
    }

    /**
     * 查询条件分组
     * @param bool $status
     * @return InterfaceBuilder
     */
    public function conditionGroup(bool $status):InterfaceBuilder{
        $this->where[] = $status?'(':')';
        $this->whereGroupCount += $status?1:-1;
        return $this;
    }

    /**
     * 设置非空查询条件
     * @param string $field
     * @param bool $equal
     * @param bool $and
     * @return InterfaceBuilder
     */
    public function conditionIsNull(string $field, bool $equal=true, bool $and=true):InterfaceBuilder{
        $sql = $this->getFieldName($field).$equal?' IS NULL':' IS NOT NULL';
        return $this->conditionSql($sql, $and);
    }

    /**
     * 设置字符串等值查询条件
     * @param string $field
     * @param string $value
     * @param bool $and
     * @return InterfaceBuilder
     */
    public function conditionEqualString(string $field, string $value, bool $and=true):InterfaceBuilder{
        $value = '\''.addslashes($value).'\'';
        $sql = $this->getFieldName($field).'='.$value;
        return $this->conditionSql($sql, $and);
    }

    /**
     * 设置整型等值查询条件
     * @param string $field
     * @param int $value
     * @param bool $and
     * @return InterfaceBuilder
     */
    public function conditionEqualInt(string $field, int $value, bool $and=true):InterfaceBuilder{
        $sql = $this->getFieldName($field).'='.$value;
        return $this->conditionSql($sql, $and);
    }

    /**
     * 设置浮点等值查询条件
     * @param string $field
     * @param float $value
     * @param bool $and
     * @return InterfaceBuilder
     */
    public function conditionEqualFloat(string $field, float $value, bool $and=true):InterfaceBuilder{
        $sql = $this->getFieldName($field).'='.$value;
        return $this->conditionSql($sql, $and);
    }

    /**
     * 设置整型大于等于比较查询条件
     * @param string $field
     * @param int $value
     * @param bool $and
     * @return InterfaceBuilder
     */
    public function conditionEqualGtInt(string $field, int $value, bool $and=true):InterfaceBuilder{
        $sql = $this->getFieldName($field).'>='.$value;
        return $this->conditionSql($sql, $and);
    }

    /**
     * 设置整型大于等于比较查询条件
     * @param string $field
     * @param int $value
     * @param bool $and
     * @return InterfaceBuilder
     */
    public function conditionEqualLtInt(string $field, int $value, bool $and=true):InterfaceBuilder{
        $sql = $this->getFieldName($field).'<='.$value;
        return $this->conditionSql($sql, $and);
    }

    /**
     * 设置模糊匹配查询条件
     * @param string $field
     * @param string $value
     * @param bool $and
     * @return InterfaceBuilder
     */
    public function conditionLike(string $field, string $value, bool $and=true):InterfaceBuilder{
        $sql = $this->getFieldName($field).' LIKE \''.addslashes($value).'\'';
        return $this->conditionSql($sql, $and);
    }


    /**
     * 增加Order排序条件
     * @param $field
     * @param bool $asc
     * @return InterfaceBuilder
     */
    public function orderBy($field, bool $asc = true):InterfaceBuilder{
        $this->order[$this->getFieldName($field, false)] = $asc?'ASC':'DESC';
        return $this;
    }

    /**
     * 构建查询语句
     * @return string
     */
    protected function buildSql():string {
        while ($this->whereGroupCount--)
            $this->where[] = ')';
        $this->whereGroupCount = 0;
        $sql = /** @lang text */'SELECT * FROM '.$this->getTableName();
        if(!empty($this->where))
            $sql .= ' WHERE '.implode(' ', $this->where);
        if(!empty($this->order)){
            $sql .= ' ORDER BY ';
            foreach($this->order as $key => $value)
                $sql .= $key.' '.$value.',';
            $sql = substr($sql,0,-1);
        }
        return $sql;
    }

    /**
     * 构造PDO结果集
     * @param string $sql
     * @return PDOStatement
     * @throws Error
     */
    protected function buildStatement(string $sql):PDOStatement{
        return $this->connect->query($sql);
    }

    /**
     * 获取查询对象
     * @param int $offset
     * @return Entity
     * @throws Runtime
     */
    public function fetch(int $offset = 0):Entity {
        $result = $this->list(1, $offset);
        if(empty($result))
            throw new Runtime('对象不存在');
        return $result[0];
    }

    /**
     * 获取查询结果集
     * @param int $number
     * @param int $offset
     * @return array|Entity[]
     */
    public function list(int $number = 0, int $offset = 0):array {
        $sql = $this->buildSql();
        if($number != 0)
            $sql .= ' LIMIT '.$offset.','.$number;
        $statement = $this->buildStatement($sql);
        $result = [];
        while ($data = $statement->fetch(PDO::FETCH_ASSOC)) {
            $entity = $this->manager->buildEntity($this->entityClass, $data);
            $entity->setLinked(true);
            $result[] = $entity;
        }
        return $result;
    }

    /**
     * 统计查询记录数
     * @return int
     */
    public function count(): int {
        $sql = $sql = $this->buildSql();
        $sql = /** @lang */'SELECT COUNT(*) FROM ('. $sql.') AS TEMP';
        $statement = $this->buildStatement($sql);
        return (int)$statement->fetch()[0];
    }

    /**
     * 获取图片集合
     * @param int $current
     * @param int $number
     * @param bool $sync
     * @return InterfacePager
     */
    public function page(int $current, int $number, bool $sync=true):InterfacePager {
        $page = new Pager();
        $page->setCurrent($current);
        $page->setNumber($number);
        $currentQuery = $this;
        $page->setCounter(function () use ($currentQuery){
            return $currentQuery->count();
        });
        $page->setLoader(function ($start, $number) use ($currentQuery){
            return $currentQuery->list($number, $start);
        });
        if($sync) $page->runSync();
        return $page;
    }

    /**
     * 调试信息输出
     * @return array
     */
    public function __debugInfo(){
        $result['name'] = $this->entityClass;
        $result['sql'] = $this->buildSql();
        return $result;
    }

}